#include "server.h"

static struct monitor_ctl monitor_ctl;
static struct listener_ctl listener_ctl;
static struct worker_ctl worker_ctl[WORKER_NUM];
static struct sender_ctl sender_ctl[SENDER_NUM];

struct share_info *share_info;

static int _check_smtp(struct host_addr_info *addrinfo)
{/*{{{*/
#define _DEF_SMTP_PORT      25
    SMTP smtp;
    int res;
    char buf[LONG_FILE_NM_SIZE+1];

    if(addrinfo->port == 0)
        addrinfo->port=_DEF_SMTP_PORT;
    sprintf(buf, "%s:%s@%s:%d", addrinfo->user, addrinfo->passwd,
            addrinfo->host, addrinfo->port);
    if((res=smtp_create(buf, &smtp)))
    {
        errlog("%s: smtp_create(%s %d) fail, res:%d",
                __func__, addrinfo->host, addrinfo->port, res);
        return(-1);
    }
    smtp_destroy(smtp);

    return(0);
}/*}}}*/

static int _cnfg(char *argv0)
{/*{{{*/
    CNFG cnfg;
    int res;
    char dirname[LONG_FILE_NM_SIZE+1];
    char file[LONG_FILE_NM_SIZE+1];
    struct cnfg_property property;
    int idx;
    //char datetmp[DATE_SIZE+1];
    //char backdir[LONG_FILE_NM_SIZE+1];
    char senddir[LONG_FILE_NM_SIZE+1];
    FILE *fp;
    DIR *dirp;
    unsigned int max_mail_seq;
    char mail_file[LONG_FILE_NM_SIZE+1];
    struct dirent dt, *dtp;
    unsigned int mail_seq;

    sig_ignore(SIGPIPE);
    ep_init();
    extract_dirname(argv0, dirname);
    ALLOC_RETERR(share_info, struct share_info, -1);

    sprintf(file, "%s/server.cnfg", dirname);
    if((res=cnfg_load(file, &cnfg)))
    {
        printf("%s: load server.cnfg fail, res:%d\n",
                __func__, res);
        if(res > 0)
            printf("%s: line %d error\n", __func__, res);
        return(-1);
    }

    CLR_BUF(share_info->_errlog_.location);
#ifdef _MY_DEBUG
    share_info->_errlog_.tracking=IS_TRUE;
    init_errlog(argv0, NULL);
#else
    // 历史
    strcpy(share_info->_errlog_.location, "_log");
    if((res=cnfg_get_property(cnfg, "ERRLOG", "LOCATION", 0, &property)))
    {
        if(res != RET_ERR_CNFG_PROPERTY_NOT_FOUND)
        {
            printf("%s: get ERRLOG.LOCATION fail, res:%d\n",
                    __func__, res);
            return(-1);
        }
    }
    else
    {
        if(!BUF_IS_CLR(property.value))
            strcpy(share_info->_errlog_.location, property.value);
    }
    if((res=init_errlog(argv0, share_info->_errlog_.location)))
    {
        printf("%s: init_errlog fail, res:%d\n", __func__, res);
        return(-1);
    }
    if((res=cnfg_get_property(cnfg, "ERRLOG", "TRACKING", 0, &property)))
    {
        if(res != RET_ERR_CNFG_PROPERTY_NOT_FOUND)
        {
            printf("%s: get ERRLOG_DETAIL fail, res:%d\n", __func__, res);
            return(-1);
        }
        share_info->_errlog_.tracking=IS_FALSE;
    }
    else
    {
        share_info->_errlog_.tracking=IS_TRUE;
    }
#endif

    // USER
    CLR_BUF(share_info->user);
    res=cnfg_get_property(cnfg, NULL, "USER", 0, &property);
    if(res)
    {
        if(res != RET_ERR_CNFG_PROPERTY_NOT_FOUND)
        {
            fprintf(stderr, "%s: get USER fail, res:%d\n",
                    __func__, res);
            return(-1);
        }
    }
    else
    {
        if(!BUF_IS_CLR(property.value))
        {
            if(strlen(property.value) > NAME_SIZE)
            {
                fprintf(stderr, "%s: USER invalid\n", __func__);
                return(-1);
            }
            strcpy(share_info->user, property.value);
        }
    }

    // 通讯
    if((res=cnfg_get_property(cnfg, "ACCESS", "SERVER_PORT", 0, &property)))
    {
        if(res != RET_ERR_CNFG_PROPERTY_NOT_FOUND)
        {
            errlog("%s: get ACCESS.SERVER_PORT fail, res:%d",
                    __func__, res);
            return(-1);
        }
        share_info->_access_.server_port=SN_MAILFWD_DEF_SERVER_PORT;
    }
    else
    {
        if(!BUF_IS_CLR(property.value)
                && (res=atoi(property.value)) >= 10000)
            share_info->_access_.server_port=res;
        else
            share_info->_access_.server_port=
                SN_MAILFWD_DEF_SERVER_PORT;
    }
    res=cnfg_get_property(cnfg, "ACCESS", "MONITOR_PORT", 0, &property);
    if(res)
    {
        if(res != RET_ERR_CNFG_PROPERTY_NOT_FOUND)
        {
            errlog("%s: get ACCESS.MONITOR_PORT fail, res:%d",
                    __func__, res);
            return(-1);
        }
        share_info->_access_.monitor_port=
            SN_MAILFWD_DEF_MONITOR_PORT;
    }
    else
    {
        if(!BUF_IS_CLR(property.value)
                && (res=atoi(property.value)) >= 10000)
            share_info->_access_.monitor_port=res;
        else
            share_info->_access_.monitor_port=
                SN_MAILFWD_DEF_MONITOR_PORT;
    }

    // 线程数量
    share_info->worker_num=WORKER_NUM;
    share_info->sender_num=SENDER_NUM;

    // 黑名单
    CLR_BUF(share_info->_mail_.black_list_file);
    if((res=cnfg_get_property(cnfg, "MAIL", "BLACK_LIST", 0, &property)))
    {
        if(res != RET_ERR_CNFG_PROPERTY_NOT_FOUND)
        {
            errlog("%s: get MAIL.BLACK_LIST fail, res:%d",
                    __func__, res);
            return(-1);
        }
    }
    else
    {
        if(!BUF_IS_CLR(property.value))
        {
            if(property.value[0] != '/')
            {
                strcpy(share_info->_mail_.black_list_file, dirname);
                strcat(share_info->_mail_.black_list_file, "/");
            }
            strcat(share_info->_mail_.black_list_file, property.value);
        }
    }
    rb_create(TREE_KEY_STRING, &share_info->black_list);
    if(!BUF_IS_CLR(share_info->_mail_.black_list_file))
    {
        char buf[BUFFER_SIZE];
        struct black *black;

        if(!(fp=fopen(share_info->_mail_.black_list_file, "r")))
        {
            errlog("%s: open black.lst fail", __func__);
            return(-1);
        }
        while(fgets(buf, sizeof(buf), fp))
        {
            buf[strlen(buf)-1]=ZERO;
            trim_head_space(buf); trim_tail_space(buf);
            if(BUF_IS_CLR(buf)) continue;
            ALLOC_DOERR(black, struct black,
                    errlog("%s: alloc black fail", __func__); return(-1));
            if(!(black->name=strdup(buf)))
            {
                errlog("%s: alloc black fail", __func__);
                return(-1);
            }
            tree_setkey_string(black, name, link);
            if(rb_insert(&share_info->black_list, &black->link))
            {
                errlog("%s: alloc black fail", __func__);
                return(-1);
            }
        }
        fclose(fp);
    }

    // 信存储目录
    res=cnfg_get_property(cnfg, "MAIL", "MAIL_STORE_DIR", 0, &property);
    if(res || BUF_IS_CLR(property.value))
    {
        errlog("%s: get MAIL.MAIL_STORE_DIR fail, res:%d",
                __func__, res);
        return(-1);
    }
    CLR_BUF(share_info->_mail_.mail_store_dir);
    if(property.value[0] != '/')
    {
        strcpy(share_info->_mail_.mail_store_dir, dirname);
        strcat(share_info->_mail_.mail_store_dir, "/");
    }
    strcat(share_info->_mail_.mail_store_dir, property.value);
    if(create_dir(share_info->_mail_.mail_store_dir) ||
            chmod(share_info->_mail_.mail_store_dir, 00777) == -1)
    {
        errlog("%s: init main_store_dir(%s) fail",
                __func__, share_info->_mail_.mail_store_dir);
    }
    sprintf(senddir, "%s/send", share_info->_mail_.mail_store_dir);
    if(create_dir(senddir) || chmod(senddir, 00777) == -1)
    {
        errlog("%s: init send dir(%s) fail", __func__, senddir);
        return(-1);
    }
    sprintf(senddir, "%s/discard", share_info->_mail_.mail_store_dir);
    if(create_dir(senddir) || chmod(senddir, 00777) == -1)
    {
        errlog("%s: init discard dir(%s) fail", __func__, senddir);
        return(-1);
    }

    // SMTP服务器地址
    res=cnfg_get_property(cnfg, "MAIL", "SMTP_ADDR", 0, &property);
    if(res || BUF_IS_CLR(property.value))
    {
        errlog("%s: get MAIL.SMTP_ADDR fail, res:%d", __func__, res);
        return(-1);
    }
    strcpy(share_info->_mail_.smtp_addr, property.value);
    if((res=host_list_parse(property.value,
                    &share_info->smtp_ready,
                    &share_info->smtp_ready_num,
                    &share_info->smtp_backup)))
    {
        errlog("%s: host_list_parse(%s) fail, res:%d",
                __func__, property.value, res);
        return(-1);
    }
    if(!share_info->smtp_ready)
    {
        errlog("%s: host_list_parse(%s) no smtp ready server",
                __func__, property.value);
        return(-1);
    }
    for(idx=0; idx<share_info->smtp_ready_num; idx++)
    {
        if(_check_smtp(&share_info->smtp_ready[idx]))
        {
            errlog("%s: _check_smtp ready(%d) fail", __func__, idx);
            return(-1);
        }
    }
    if(share_info->smtp_backup && _check_smtp(share_info->smtp_backup))
    {
        errlog("%s: _check_smtp backup fail", __func__);
        return(-1);
    }

    // VIP SMTP服务器地址
    if((res=cnfg_get_property(cnfg, "MAIL", "SMTP_ADDR_VIP", 0, &property)))
    {
        if(res != RET_ERR_CNFG_PROPERTY_NOT_FOUND)
        {
            errlog("%s: get MAIL.SMTP_ADDR_VIP fail, res:%d",
                    __func__, res);
            return(-1);
        }
        strcpy(share_info->_mail_.smtp_addr_vip,
                share_info->_mail_.smtp_addr);
    }
    else
    {
        if(!BUF_IS_CLR(property.value))
            strcpy(share_info->_mail_.smtp_addr_vip, property.value);
        else
            strcpy(share_info->_mail_.smtp_addr_vip,
                    share_info->_mail_.smtp_addr);
    }
    if((res=host_list_parse(share_info->_mail_.smtp_addr_vip,
                    &share_info->smtp_ready_vip,
                    &share_info->smtp_ready_vip_num,
                    &share_info->smtp_backup_vip)))
    {
        errlog("%s: host_list_parse(%s) fail, res:%d",
                __func__, share_info->_mail_.smtp_addr_vip, res);
        return(-1);
    }
    if(!share_info->smtp_ready_vip)
    {
        errlog("%s: host_list_parse(%s) no vip smtp ready server",
                __func__, property.value);
        return(-1);
    }
    for(idx=0; idx<share_info->smtp_ready_vip_num; idx++)
    {
        if(_check_smtp(&share_info->smtp_ready_vip[idx]))
        {
            errlog("%s: _check_smtp vip ready(%d) fail",
                    __func__, idx);
            return(-1);
        }
    }
    if(share_info->smtp_backup_vip &&
            _check_smtp(share_info->smtp_backup_vip))
    {
        errlog("%s: _check_smtp vip backup fail", __func__);
        return(-1);
    }

    cnfg_free(cnfg);

    // stat
    pthread_mutex_init(&share_info->stat_lock, NULL);
    ALLOC_MULTI_RETERR(share_info->worker_stat, struct _worker_stat,
            share_info->worker_num, -1);
    for(idx=0; idx<share_info->worker_num; idx++)
    {
        share_info->worker_stat[idx].slot_used_num=1;
    }
    ALLOC_MULTI_RETERR(share_info->sender_stat, struct _sender_stat,
            share_info->sender_num, -1);
    for(idx=0; idx<share_info->sender_num; idx++)
    {
        share_info->sender_stat[idx].send_num=0;
        share_info->sender_stat[idx].todo_num=0;
    }

    // sender queue
    ALLOC_MULTI_RETERR(share_info->sender_msgq, THRD_MSGQ,
            share_info->sender_num, -1);
    for(idx=0; idx<share_info->sender_num; idx++)
        thrd_msgq_create(&share_info->sender_msgq[idx]);

    pthread_mutex_init(&share_info->mail_id_lock, NULL);
    curr_date(share_info->curr_dt);

    // 读取待发送邮件
    for(idx=0; idx<share_info->sender_num; idx++)
        listp_init(&sender_ctl[idx].msgq_todo);
    sprintf(senddir, "%s/send", share_info->_mail_.mail_store_dir);
    idx=0;
    max_mail_seq=0;
    dirp=opendir(senddir);
    while(1)
    {
        struct stat st;
        char *p;
        struct mail *mail;

        readdir_r(dirp, &dt, &dtp);
        if(!dtp)  break;
        if(strcmp(dtp->d_name, ".") == 0 ||
                strcmp(dtp->d_name, "..") == 0 ||
                ((res=strlen(dtp->d_name)) != MAIL_FILENAME_SIZE &&
                 res != MAIL_VIP_FILENAME_SIZE))
            continue;
        // yyyymmdd_nnnnnnnnn[_vip].txt
        if(strncmp(dtp->d_name, share_info->curr_dt, DATE_SIZE) > 0)
        {
            errlog("%s: send mail file(%s) date error",
                    __func__, dtp->d_name);
            return(-1);
        }
        strncpy(mail_file, dtp->d_name+DATE_SIZE+1, MAIL_SEQ_SIZE);
        mail_file[MAIL_SEQ_SIZE]=ZERO;
        if(strncmp(dtp->d_name, share_info->curr_dt, DATE_SIZE) == 0 && 
                (mail_seq=atoi(mail_file)) > max_mail_seq)
            max_mail_seq=mail_seq;
        sprintf(mail_file, "%s/%s", senddir, dtp->d_name); 
        if((fp=fopen(mail_file, "r")) == NULL)
        {
            errlog("%s: open mail_file(%s) fail", __func__, mail_file);
            return(-1);
        }
        fstat(fileno(fp), &st);
        if(!S_ISREG(st.st_mode) || !(st.st_mode & S_IRUSR) ||
                !(st.st_mode & S_IWUSR))
        {
            errlog("%s: mail_file(%s) attr error", __func__, mail_file);
            return(-1);
        }
        ALLOC_DOERR(mail, struct mail,
                errlog("%s: alloc mail fail", __func__); return(-1));
        ALLOC_MULTI_DOERR(mail->body, char, st.st_size+1,
                errlog("%s: alloc mail body fail"); return(-1));
        mail->try_times=0;
        strcpy(mail->file, dtp->d_name);
        fgets(mail->from, sizeof(mail->from), fp);
        mail->from[strlen(mail->from)-1]=ZERO;
        fgets(mail->rcpt, sizeof(mail->rcpt), fp);
        mail->rcpt[strlen(mail->rcpt)-1]=ZERO;
        fgets(mail->subject, sizeof(mail->subject), fp);
        mail->subject[strlen(mail->subject)-1]=ZERO;
        for(p=mail->body; fgets(p, st.st_size, fp); p+=strlen(p));
        if(ferror(fp))
        {
            errlog("%s: mail_file(%s) read fail", __func__, mail_file);
            return(-1);
        }
        fclose(fp);
        if(strncmp(dtp->d_name+DATE_SIZE+1+MAIL_SEQ_SIZE,
                    "_vip", strlen("_vip")) == 0)
            idx=SENDER_VIP_IDX;
        else
            idx=random_int(1, share_info->sender_num-1, NULL);
        listp_add_tail(&mail->link, &sender_ctl[idx].msgq_todo);
    }
    closedir(dirp);
    share_info->mail_id=max_mail_seq+1;

    return(0);
}/*}}}*/

static int _init()
{/*{{{*/
    int res;
    int idx;
    int i;
    struct host_addr_info *addrinfo;
    char buf[LONG_FILE_NM_SIZE+1];
    SOCKET sock;
    SOCKET sock_pair[2];
    struct worker_ctl *p_worker_ctl;
    struct sender_ctl *p_sender_ctl;
    int smtp_ready_num;
    struct host_addr_info *smtp_ready;
    struct host_addr_info *smtp_backup;

    if(getuid() == 0)
    {
        struct passwd *user=getpwnam(share_info->user);
        if(user)
        {
            if(setuid(user->pw_uid) == -1)
            {
                fprintf(stderr, "Change to user %s fail\n",
                        share_info->user);
                return(-1);
            }
            fprintf(stderr, "Change to user %s\n", share_info->user);
        }
        else
        {
            fprintf(stderr, "User %s not found, keep root\n",
                    share_info->user);
        }
    }

    /*----- 监控线程 -----*/
    ALLOC_MULTI_RETERR(monitor_ctl.msg_in, char, BUFFER_SIZE, -1);
    ALLOC_MULTI_RETERR(monitor_ctl.msg_out, char, BUFFER_SIZE, -1);
    sprintf(buf, "%d", share_info->_access_.monitor_port);
    if((res=skt_listen(buf, 0, &monitor_ctl.skt)))
    {
        errlog("%s: open monitor port fail", __func__);
        return -1;
    }
    errlog("%s: open monitor port(%d)",
            __func__, share_info->_access_.monitor_port);

    /*----- 监听线程 -----*/
    // 初始化通信槽
    if((res=ep_create(&listener_ctl.ep)))
    {
        errlog("%s: listener(ep_create) fail, res:%d", __func__, res);
        return(-1);
    }

    // 开启接入端口
    if((sock=sock_listen(share_info->_access_.server_port)) ==
            INVALID_SOCKET)
    {
        errlog("%s: open server port fail", __func__);
        return(-1);
    }
    if((res=ep_slot_register(listener_ctl.ep, sock, IS_TRUE,
                    &listener_ctl.listener_slot)))
    {
        errlog("%s: listener(ep_slot_register) server port fail, res:%d",
                __func__, res);
        return(-1);
    }
    errlog("%s: open server port(%d)",
            __func__, share_info->_access_.server_port);

    /*----- 工作线程 -----*/
    // 工作线程数量
    for(idx=0; idx<share_info->worker_num; idx++)
    {
        p_worker_ctl=&worker_ctl[idx];

        // 开启监听和工作线程间通道
        if(sock_local_pair(sock_pair))
        {
            errlog("%s: open listener-worker socket pair fail",
                    __func__);
            return(-1);
        }
        if((res=ep_slot_register(listener_ctl.ep, sock_pair[0], IS_FALSE,
                        &listener_ctl.worker_slot[idx])))
        {
            errlog("%s: listener(ep_register) worker(%d) fail, res:%d",
                    __func__, idx, res);
            return(-1);
        }
        ep_slot_set_wrap(listener_ctl.worker_slot[idx]);
        if((res=ep_create(&p_worker_ctl->ep)))
        {
            errlog("%s: worker(%d)(ep_create) fail, res:%d",
                    __func__, idx, res);
            return(-1);
        }
        if((res=ep_slot_register(p_worker_ctl->ep, sock_pair[1], IS_FALSE,
                        &p_worker_ctl->listener_slot)))
        {
            errlog("%s: worker(%d)(ep_slot_register) listener fail, "
                    "res:%d",
                    __func__, idx, res);
            return(-1);
        }
        ep_slot_set_wrap(p_worker_ctl->listener_slot);

        // 启动工作线程
        p_worker_ctl->worker_idx=idx;
        ALLOC_MULTI_RETERR(p_worker_ctl->msg_in, char, SOCK_MSG_SIZE, -1);
        ALLOC_MULTI_RETERR(p_worker_ctl->msg_out, char, SOCK_MSG_SIZE, -1);
        if(thrd_create((THRD_FUNC_T)thrd_worker, (void *)p_worker_ctl,
                    THRD_DETACH, &p_worker_ctl->thrd_id))
        {
            errlog("%s: worker(%d) thrd create fail", __func__, idx);
            return(-1);
        }

        // 修改共享信息
        //share_info->worker_stat[idx].stat=_RUNNING_;
        share_info->worker_stat[idx].slot_used_num=1;

        errlog("%s: worker(%d) thrd create", __func__, idx);
    }

    /*----- 启动接入线程 -----*/
    if(thrd_create((THRD_FUNC_T)thrd_listener, (void *)&listener_ctl,
                THRD_DETACH, &listener_ctl.thrd_id))
    {
        errlog("%s: listener thrd create fail", __func__);
        return(-1);
    }
    errlog("%s: listener thrd create", __func__);

    /*----- 启动发送线程 -----*/
    for(idx=0; idx<share_info->sender_num; idx++)
    {
        if(idx == SENDER_VIP_IDX)
        {
            smtp_ready_num=share_info->smtp_ready_vip_num;
            smtp_ready=share_info->smtp_ready_vip;
            smtp_backup=share_info->smtp_backup_vip;
        }
        else
        {
            smtp_ready_num=share_info->smtp_ready_num;
            smtp_ready=share_info->smtp_ready;
            smtp_backup=share_info->smtp_backup;
        }
        p_sender_ctl=&sender_ctl[idx];
        p_sender_ctl->sender_idx=idx;
        ALLOC_MULTI_RETERR(p_sender_ctl->smtp_ready, SMTP,
                share_info->smtp_ready_vip_num, -1);

        // 初始化smtp
        for(i=0; i<smtp_ready_num; i++)
        {
            addrinfo=&smtp_ready[i];
            sprintf(buf, "%s:%s@%s:%d",
                    addrinfo->user, addrinfo->passwd,
                    addrinfo->host, addrinfo->port);
            if((res=smtp_create(buf, &p_sender_ctl->smtp_ready[i])))
            {
                errlog("%s: smtp_create(%s:%d) fail, res:%d",
                        __func__, addrinfo->host, addrinfo->port,
                        res);
                return(-1);
            }
        }
        if(smtp_backup)
        {
            addrinfo=smtp_backup;
            sprintf(buf, "%s:%s@%s:%d",
                    addrinfo->user, addrinfo->passwd,
                    addrinfo->host, addrinfo->port);
            if((res=smtp_create(buf, &p_sender_ctl->smtp_backup)))
            {
                errlog("%s: smtp_create backup(%s:%d) fail, res:%d",
                        __func__, addrinfo->host, addrinfo->port, res);
                return(-1);
            }
        }

        // 启动
        if(thrd_create((THRD_FUNC_T)thrd_sender, (void *)p_sender_ctl,
                    THRD_DETACH, &p_sender_ctl->thrd_id))
        {
            errlog("%s: sender(%d) thrd create fail", __func__, idx);
            return(-1);
        }

        errlog("%s: sender(%d) thrd create", __func__, idx);
    }

    return(0);
}/*}}}*/

int main(int argc, char *argv[])
{/*{{{*/
#ifndef _MY_DEBUG
    daemon(1, 1);
#endif

    if(_cnfg(argv[0]))
        return(-1);
    if(_init())
        return(-2);
    if(thrd_monitor(&monitor_ctl))
        return(-3);

    return(0);
}/*}}}*/
